package com.derbysoft.ota.application.translator.rq;

import java.util.List;

import javax.annotation.Nullable;

import org.w3c.dom.Element;

import com.derbysoft.modules.eip.translator.Translator;
import com.derbysoft.ota.application.translator.MetaDatas;
import com.derbysoft.ota.application.util.Functions;
import com.derbysoft.ota.application.util.ParagraphTypes;
import com.derbysoft.ota.interfaces.protocol._2009a.BasicPropertyInfoType;
import com.derbysoft.ota.interfaces.protocol._2009a.CustomerType;
import com.derbysoft.ota.interfaces.protocol._2009a.GuaranteeType;
import com.derbysoft.ota.interfaces.protocol._2009a.HotelResRequestType;
import com.derbysoft.ota.interfaces.protocol._2009a.HotelReservationType;
import com.derbysoft.ota.interfaces.protocol._2009a.ResGlobalInfoType;
import com.derbysoft.ota.interfaces.protocol._2009a.ResGuestsType;
import com.derbysoft.ota.interfaces.protocol._2009a.RoomStaysType;
import com.derbysoft.ota.interfaces.protocol._2009a.CommentType.Comment;
import com.derbysoft.ota.interfaces.protocol._2009a.ResGuestsType.ResGuest;
import com.derbysoft.ota.interfaces.protocol._2009a.RoomStaysType.RoomStay;
import com.derbysoft.remote.dswitch.generic.dto.CustomerDTO;
import com.derbysoft.remote.dswitch.generic.dto.GuaranteeDTO;
import com.derbysoft.remote.dswitch.hotel.constants.PaymentType;
import com.derbysoft.remote.dswitch.hotel.dto.HotelRefDTO;
import com.derbysoft.remote.dswitch.hotel.dto.HotelReservationDTO;
import com.derbysoft.remote.dswitch.hotel.dto.ReservationRoomStayDTO;
import com.derbysoft.remote.share.LanguageType;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.googlecode.functionalcollections.FunctionalIterables;

public class HotelResRequestTypeTranslator implements Translator<HotelResRequestType, HotelReservationDTO> {

	private Translator<GuaranteeType, GuaranteeDTO> guaranteeRQTranslator;
	private Translator<RoomStay, ReservationRoomStayDTO> roomStayTranslator;
	private Translator<CustomerType, CustomerDTO> guestTranslator;
	private Translator<String, LanguageType> languageTypeTranslator;
	
	@Inject
	public HotelResRequestTypeTranslator(
		Translator<GuaranteeType, GuaranteeDTO> guaranteeRQTranslator,
		Translator<RoomStay, ReservationRoomStayDTO> roomStayTranslator,
		Translator<CustomerType, CustomerDTO> guestTranslator,
		Translator<String, LanguageType> languageTypeTranslator) {
		
		this.guaranteeRQTranslator = guaranteeRQTranslator;
		this.roomStayTranslator = roomStayTranslator;
		this.guestTranslator = guestTranslator;
		this.languageTypeTranslator = languageTypeTranslator;
	}

	@Override
	public HotelReservationDTO translate(HotelResRequestType request) {
		HotelReservationDTO switchHotelReservationDTO = new HotelReservationDTO();
		HotelReservationType source = FunctionalIterables.make(
			request.getHotelReservations().getHotelReservation()
		).messagedIterator("Please provide us exactly 1 HotelReservation.").next();

		ResGlobalInfoType resGlobalInfo = source.getResGlobalInfo();
		if (resGlobalInfo != null) {
			switchHotelReservationDTO.setComments(comments(resGlobalInfo));
			switchHotelReservationDTO.setGuarantees(guarantees(resGlobalInfo));
		}

		RoomStaysType roomStays = source.getRoomStays();
		Preconditions.checkNotNull(roomStays, "RoomStays required.");
		RoomStay roomStay = FunctionalIterables.make(roomStays.getRoomStay()).messagedIterator("Please provide us exactly 1 RoomStay.").next();
		switchHotelReservationDTO.setHotelRef(hotelRef(roomStay));
		switchHotelReservationDTO.setRoomStays(roomStays(roomStays));
		
		Preconditions.checkNotNull(source.getResGuests(), "Guests required.");
		switchHotelReservationDTO.setGuests(guests(source.getResGuests()));
		switchHotelReservationDTO.setContactPersons(contactPersons(switchHotelReservationDTO));
		
		switchHotelReservationDTO.setPaymentType(paymentType(source));
		switchHotelReservationDTO.setLanguageType(languageType(request));
		return switchHotelReservationDTO;
	}

	private List<CustomerDTO> contactPersons(HotelReservationDTO switchHotelReservationDTO) {
		return FunctionalIterables.make(switchHotelReservationDTO.getGuests()).map(new Function<CustomerDTO, CustomerDTO>() {
			public CustomerDTO apply(CustomerDTO from) {
				return (CustomerDTO) from.clone();
			}
		}).toList();
	}

	private LanguageType languageType(HotelResRequestType request) {
		return languageTypeTranslator.translate(
			Preconditions.checkNotNull(request.getPrimaryLangID(), "PrimaryLangID required.")
		);
	}

	private List<String> comments(ResGlobalInfoType globalInfoType) {
		if (globalInfoType.getComments() == null) {
			return Lists.newArrayList();
		}
		return FunctionalIterables.make(globalInfoType.getComments().getComment()).map(
			new Function<Comment, String>() {
				public String apply(@Nullable Comment from) {
					return ParagraphTypes.toString(from.getTextOrImageOrURL());
				}
			}
		).toList();
}

	private List<GuaranteeDTO> guarantees(ResGlobalInfoType globalInfoType) {
		if (globalInfoType.getGuarantee() == null) {
			return Lists.newArrayList();
		}
		return Lists.newArrayList(guaranteeRQTranslator.translate(globalInfoType.getGuarantee()));
	}

	private HotelRefDTO hotelRef(RoomStay roomStay) {
		BasicPropertyInfoType basicPropertyInfo = roomStay.getBasicPropertyInfo();
		Preconditions.checkNotNull(basicPropertyInfo, "BasicPropertyInfo required.");
		HotelRefDTO hotelRefDTO = new HotelRefDTO();
		hotelRefDTO.setCode(basicPropertyInfo.getHotelCode());
		hotelRefDTO.setName(basicPropertyInfo.getHotelName());
		hotelRefDTO.setBrandCode(basicPropertyInfo.getBrandCode());
		hotelRefDTO.setChainCode(basicPropertyInfo.getChainCode());
		return hotelRefDTO;
	}

	private List<ReservationRoomStayDTO> roomStays(RoomStaysType roomStays) {
		if (roomStays.getRoomStay().isEmpty()) {
			return Lists.newArrayList();
		}
		return FunctionalIterables.make(roomStays.getRoomStay()).map(Functions.of(roomStayTranslator)).toList();
	}

	private List<CustomerDTO> guests(ResGuestsType resGuests) {
		// resGuests should never be empty, but... for test convenience, we allow it be empty here :(
		if (resGuests.getResGuest().isEmpty()) {
			return Lists.newArrayList();
		}
		return FunctionalIterables.make(resGuests.getResGuest()).map(
			new Function<ResGuest, CustomerDTO>() {
				public CustomerDTO apply(@Nullable ResGuest from) {
					return guestTranslator.translate(customer(from));
				}

				private CustomerType customer(ResGuest from) {
					return FunctionalIterables.make(
						Preconditions.checkNotNull(from.getProfiles(), "Profiles required.").getProfileInfo()
					).messagedIterator("Please provide us exactly 1 profile info in ResGuest.")
					.next().getProfile().getCustomer();
				}
		}).toList();
}
	
	private PaymentType paymentType(HotelReservationType source) {
		Preconditions.checkNotNull(source.getTPAExtensions(), "TPAExtensions of HotelReservationType required.");
		Element detected = FunctionalIterables.make(source.getTPAExtensions().getAny()).detect(
			new Predicate<Element>() {
				public boolean apply(Element input) {
					return input.getTagName().equals(MetaDatas.PAYMENT_TYPE_EXTENSION_KEY);
				}
			}
		);
		Preconditions.checkNotNull(detected, "Please provide us the PaymentType in TPAExtensions of HotelReservationType.");
		return PaymentType.valueOf(detected.getTextContent());
	}
}
